package tomasulo;

class Reservoir {

    static final int AVAILABLE = 0;
    static final int WAITING = 1;
    static final int READY = 2;
    static final int EXEING = 3;
    int ctrl; // 
    int stationNo1;
    int stationNo2;
    double op1;
    double op2;
    boolean ready1;
    boolean ready2;
    int exeClick; // 计算执行的时间
    int totalClick; // 计算总共应该执行的时间
    int type; // 运算的类型
    int instrIdx; // 保存的运算指令在queue中的下标
    int clicks[] = {2, 2, 10, 40};

    Reservoir() {
        ctrl = AVAILABLE; // 空闲
        this.exeClick = 0;
    }

    void clear() {
        ctrl = AVAILABLE;
        stationNo1 = stationNo2 = 0;
        op1 = op2 = 0;
        ready1 = ready2 = false;
        exeClick = 0;
    }

    void addTask(int type, int no1, int no2, int instrIdx) { // 添加运算任务	
        this.type = type;
        this.totalClick = clicks[type];
        this.stationNo1 = no1;
        this.stationNo2 = no2;
        this.instrIdx = instrIdx;
        ready1 = false;
        ready2 = false;
        ctrl = WAITING;
        checkSource(); // 看寄存器的值是否能取到
    }

    void checkSource() {

        if (!Kernel.kernel.register.busy[stationNo1]) { // 如果该寄存器空闲，则取得立即数

            op1 = Kernel.kernel.register.val[stationNo1];
            ready1 = true;
        } else {
            // 如果忙碌，将寄存器保存的站号替换掉寄存器的编号

            stationNo1 = Kernel.kernel.register.stationNo[stationNo1];
        }
        if (!Kernel.kernel.register.busy[stationNo2]) { 

            op2 = Kernel.kernel.register.val[stationNo2];
            ready2 = true;
        } else {

            stationNo2 = Kernel.kernel.register.stationNo[stationNo2];
        }
        if (ready1 && ready2) {
            ctrl = READY;
        }
        
    }
}

class Execute {

    Reservoir res[];
    boolean busy; // 是否在执行任务
    int busyResNo;
    int startStationNo; // 第一个保存站的编号

    public void dispatch(int stationNo, double val) {
        // TODO Auto-generated method stub
        System.out.println("[Execute.dispatch]");
        for (int i = 0; i < res.length; i++) {
            if (res[i].ctrl == Reservoir.WAITING) { // 如果保存站空闲
                if (res[i].stationNo1 == stationNo) {
                    res[i].op1 = val;
                    res[i].ready1 = true;
                }
                if (res[i].stationNo2 == stationNo) {
                    res[i].op2 = val;
                    res[i].ready2 = true;
                }
                if (res[i].ready1 && res[i].ready2) {
                    res[i].ctrl = Reservoir.READY;
                }
            }
            /*System.out.print(i + " value " + ":" + res[i].stationNo1 + " "
            + res[i].stationNo2 + " ^^ " + res[i].ctrl + " "
            + res[i].op1 + " " + res[i].op2 + " ||");*/

        }
    }

    void clear() {
        busy = false;
        for (int i = 0; i < res.length; i++) {
            res[i].clear();
        }
    }

    double execute(double op1, double op2, int type) {
        switch (type) {
            case 0:
                return op1 + op2;
            case 1:
                return op1 - op2;
            case 2:
                return op1 * op2;
            case 3:
                return op1 / op2;
            default:
                System.out.println("Strange Exe Operation Type");
                return 0;
        }
    }

    void click() {
        System.out.println("[Execute.click]\n");
        if (busy) { // 如果运算器有任务在执行
            //System.out.println(res[busyResNo].exeClick);
            res[busyResNo].exeClick++;
            if (res[busyResNo].exeClick == res[busyResNo].totalClick) { // 命令执行完毕
                double result = execute(res[busyResNo].op1, res[busyResNo].op2,
                        res[busyResNo].type);
                Kernel.kernel.dispatch(startStationNo + busyResNo, result);
                Kernel.kernel.manager.setState(res[busyResNo].instrIdx,
                        Instruction.FINISHED);
                Kernel.kernel.manager.setFinishTime(res[busyResNo].instrIdx,
                        Kernel.kernel.cycle + 1);
                res[busyResNo].ctrl = Reservoir.AVAILABLE;
                busy = false;
                Kernel.kernel.manager.queue.get(res[busyResNo].instrIdx).setState(Instruction.FINISHED);
                /*System.out.println(busyResNo + " " + res[busyResNo].op1 + " "
                + res[busyResNo].op2 + " " + result + " "
                + res[busyResNo].type);*/
            }
        } else { // 运算器空闲，添加进新的任务
            for (int i = 0; i < res.length; i++) {
                if (res[i].ctrl == Reservoir.READY) {
                    res[i].ctrl = Reservoir.EXEING;
                    res[i].exeClick++;
                    busy = true;
                    busyResNo = i;
                    break;
                    // TODO 锟睫革拷指锟斤拷状态
                }
            }
        }
    }

    public int addTask(Instruction instr, int instrIdx) { 
		// 添加新的运算任务
        for (int i = 0; i < res.length; i++) {
            if (res[i].ctrl == Reservoir.AVAILABLE) { // 保存站空闲
                res[i].addTask(instr.type, instr.op1, instr.op2, instrIdx);
                return startStationNo + i;
            }
        }
        return -1;
    }

    String[][] toStrings() {
        String[][] ret = new String[res.length][8];
        for (int i = 0; i < res.length; i++) {
            if (res[i].ctrl != Reservoir.AVAILABLE) {
                ret[i][0] = String.valueOf(res[i].exeClick);
                if (res[i].type == 0 || res[i].type == 1) {
                    ret[i][1] = "ADD" + (i + 1);
                } else {
                    ret[i][1] = "MUL" + (i + 1);
                }
                if (res[i].ctrl == Reservoir.AVAILABLE) {
                    ret[i][2] = "False";
                } else {
                    ret[i][2] = "True";
                }
                ret[i][3] = Instruction.OPE[res[i].type];
                if (res[i].ready1) {
                    ret[i][4] = String.valueOf(res[i].op1);
                    ret[i][6] = "";
                } else {
                    ret[i][4] = "";
                    ret[i][6] = String.valueOf(res[i].stationNo1);
                }
                if (res[i].ready2) {
                    ret[i][5] = String.valueOf(res[i].op2);
                    ret[i][7] = "";
                } else {
                    ret[i][5] = "";
                    ret[i][7] = String.valueOf(res[i].stationNo2);
                }
            } else {
                ret[i][0] = ret[i][3] = ret[i][4] = ret[i][5] = ret[i][6] = ret[i][7] = "";
                if (res[i].type == 0 || res[i].type == 1) {
                    ret[i][1] = "ADD" + (i + 1);
                } else {
                    ret[i][1] = "MUL" + (i + 1);
                }
                ret[i][2] = "False";
            }
        }
        return ret;
    }

    String instrState(int stationNo) {
        int state = res[stationNo - startStationNo].ctrl;
        switch (state) {
            case Reservoir.WAITING:
                return "WAITING";
            case Reservoir.READY:
                return "READY";
            case Reservoir.EXEING:
                return "EXEING";
            default:
                return "";
        }
    }

    Execute(int reserverCnt, int startStationNo) {
        this.res = new Reservoir[reserverCnt];
        for (int i = 0; i < res.length; i++) {
            this.res[i] = new Reservoir();
        }
        this.startStationNo = startStationNo;
        busy = false;
        busyResNo = -1;
    }
}

class Add extends Execute {

    static final int ADD_RES = 3;
    static final int START_STATIONNO = 6;

    Add() {
        super(ADD_RES, START_STATIONNO);
    }
}

class Mul extends Execute {

    static final int MUL_RES = 2;
    static final int START_STATIONNO = 4;

    Mul() {
        super(MUL_RES, START_STATIONNO);
    }
}